DisplayList für Neueinsteiger -1

← Zurück zum Forum

DisplayList #1

Wenn man (so wie ich damals in den 80-igern) Programme in einer Hochsprache entwickelt - damals auf dem Atari 800 z.B. in BASIC, dann ist man nicht unbedingt darauf angewiesen ein tieferes Verständnis der grafischen Bildschirmausgabe zu besitzen. Das BASIC hat bereits entsprechende Befehle integriert und übersetzt am Ende alles in den entsprechenden Maschinencode. So ist es dann einfach z.B. eine „Hallo Welt”- Ausgabe zu programmieren:

10 Print „Hallo Welt”
20 Goto 10

Ein entsprechender Print-Befehl existiert in Assembler nicht und wenn man etwas auf den Bildschirm bekommen möchte, dann stößt man recht bald unweigerlich auf die DisplayList. So erging es auch mir! Aber was genau passiert denn da? Der Name zunächst ist völlig irreführend, denn es handelt sich dabei gar nicht um eine Bildschirm Liste, sondern um Programmanweisungen eines ganz eigenen Prozessors - dem ANTIC. Vielfach gehört, aber wie genau erzeugt dieser Chip das Bild und wie kann ich das beeinflussen und steuern?

Die Idee eines solchen Prozessors war zwar damals nicht neu, für den Heimcomputerbereich aber schon fast revolutionär. Der ANTIC liest dabei Daten aus dem Hauptspeicher, verarbeitet sie und baut dann entsprechend den Bildschirm auf. Zusätzlich sorgt der ANTIC-Prozessor auch für die Generierung der Player-/Missile Grafik. Der Antik kann nur lesen und keine Daten in den Prozessor zurückschreiben, um dieses Problem zu lösen gibt es die Schattenregister…

Die Befehle, die der Anti verarbeiten kann sind eigentlich recht simpel. Es gibt einen Vectorbefehl, wo angegeben ist, wo der Bildschirm anfängt und einen Sprungbefehl, der saget, wo die Displaylist wieder startet. Alle anderen Displaylisteinträge sind keine Befehle im eigentlichen Sinn, sondern stellen nur Werte dar, wie die einzelnen Zeilen aufgebaut werden.

Wenn man jetzt weiß, an welcher Speicherstelle der Bildschirm bei Graphics 0 (in Basic) anfängt, kann man relativ einfach durch ein Poke-Befehl im Basic oder durch LDA / STA in Assembler eine Ausgabe auf den Bildschirm machen: Zum Experimentieren empfehle ich das in Basic, das kann man dann relativ einfach in Assembler umsetzen:

Poke 40000,33 läßt links oben ein A entstehen, denn dort fängt der Bildschirm im Speicher an… und dieser Anfang wird in der DL festgelegt!

In den Speicherstellen 560 (LoBi) und 561 (HiBi) steht die Adresse des DL: Print Peek(560) 32 Print Peek(561) 156 Print 32+256*156 39968

Am Beispiel von Graphics 0 fängt die DL also an der Stelle 39968 im Hauptspeicher an. Hier geht es los mit drei Einträgen 112 ? Peek(39968), Peek(39969), Peek(39970) 112 112 112 Das sind die drei schwarzen Zeilen am oberen Rand, die einfach leer sind. Poke 39968,2 würde eine leere Textzeile am Oberrad erzeugen (die man aber nicht beschreiben kann).

An Stelle 4 des DL (also bei Speicherstelle 39971 kommt ein erster echter Befehl: 64 - dieser benennt die Adresse des Bildschirmstartes mit den zwei folgenden Stellen (Stelle 5 und 6): Peek(39972) liefert 64 und Peek(39973) liefert 156

64+256*156 ergibt 40000 - also die Startadresse des Bildschirmes. Will man also die Startadresse des Bildschirmes ändern (wird z.B. scrollen wichtig), dann ändert man einfach diese beiden Adressen entsprechend!

Danach folgen 23 x die Speichereinträge 2 - diese legen letztlich den Textmodus für Graphics 0 fest und danach kommt der Sprungbefehl (65) mit den nachfolgenden beiden Speichereinträgen, die festlegen wohin gesprungen werden soll - nämlich wieder zum Anfang der DL, die ja ständig wiederholt abgearbeitet werden muss. Warum nur 23x2 ? Wir haben ja 24 Zeilen! Das steckt witzigerweise in Stelle 4 der DL im Befehl mit drin. Der Befehl an Stelle 4 der DL ist eigentlich Dezimal 64, in der Stelle 4 steht aber 66. Hier wird einfach schon die erste zu generierende Zeile zum Befehl addiert. 64+2=66 Ich denke, die Atari-Entwickler wollten damit ein wenig Verwirrung stiften 😉

Das wars eigentlich schon, was den Aufbau der DL angeht. Im Anhang noch eine schöne Übersicht über die DL bei Graphic 0.

In Teil 2 wird man dann erfahren, was es für Auswirkungen haben kann die DL zu verändern, wie flexibel man alles mischen kann und wie man einfach eigene Grafikmodi erstellen kann.

Anhänge:
AntwortZitat

Hier noch kurz mein Listing für "Hallo Welt":

Basic:

10 Print "Hallo Welt"
20 Goto 10

Assembler:

10 ;Hallo Welt Demo einfach
20    *= $4000
30    LDA #40
40    STA $9C40
50    LDA #33
60    STA $9C41
70    LDA #44
80    STA $9C42
90    STA $9C43
100   LDA #47
110   STA $9C44
120   LDA #55
130   STA $9C46
140   LDA #37
150   STA $9C47
160   LDA #44
170   STA $9C48
180   LDA #52
190   STA $9C49
200   LDA #00
210   STA $9C45
220   STA $9C4A

Dieser Assemblercode ist hier bewusst ganz einfach gewählt! Etwas komplexer wird es dann hier:

10 ;Hallo Welt Demo Komplex
20    .OPT OBJ
30    *= $4000
40 RET = $9B
50 IOCB = $0340
60 CIOV = $E456
70 DEVICE .BYTE "E:",RET
80 TEXT .BYTE "HALLO WELT!",RET
90    LDX #$20
100   LDA #$03
110   STA $0342,X
120   LDA #DEVICE&255
130   STA $0344,X
140   LDA #DEVICE/256
150   STA $0345,X
160   LDA #$08
170   STA $034A,X
180   LDA #$00
190   STA $034B,X
200   JSR CIOV
210   LDA #$09
220   STA $0342,X
230   LDA #TEXT&255
240   STA $0344,X
250   LDA #TEXT/256
260   STA $0345,X
270   LDA #$00
280   STA $0348,X
290   LDA #$FF
300   STA $0349,X
310   LDA #$FF
320   STA $02FC
330 LP  JSR CIOV
340   LDA $02FC
350   CMP #$FF
360   BNE STOP
370   JMP LP
380 STOP  BRK
390   .END

Das Programm startet nicht bei 4000, sondern bei 400F !! Man sieht an diesem Beispiel wie komplex die Programmierung einfacher Anwendungen in Assembler werden kann. Zur Ausgabe von Hallo Welt wurde zudem in diesem Beispiel die CIOV des Ataris benutzt, man könnte den Text natürlich auch direkt auf den Bildschirm ausgeben - was allerdings in diesem Fall etwas weniger elegant ist. Dies würde dann den Text "Hallo Welt!" quasi Buchstabe für Buchstabe zerlegen und dann in den Bildschirmspeicher direkt kopieren - ähnlich wie im ersten etwas plumpen Listing.

AntwortZitat

Prodehl schrieb: Leider macht dieser Editor alle Tabs weg, die müssen natürlich im Assembler rein!

Umschliesse Quellcode mit [ code ] [ /code ] Bloecken (nur ohne die Leerzeichen hinter [ und ]), z.B.:

10 ;Hallo Welt Demo einfach
20    *= $4000
30    LDA #40
40    STA $9C40
50    LDA #33
60    STA $9C41
70    LDA #44
80    STA $9C42
90    STA $9C43
[...]

Dann sieht es besser aus ;)

Prodehl schrieb: Wenn man dies jetzt per copy/paste in den Assembler überträgt, dann werden die Anführungszeichen nicht mitkopiert ...

In deinen Beispielen sind typografische Anfuehrungszeichen, wie sie von Textverarbeitungen erzeugt werden. Die kennt der Atari nicht, daher verschwinden die. Ggf. oben im Post durch echte Anfuehrungszeichen "" ersetzen.

https://mastodon.social/@cstrotm https://abbuc.social

AntwortZitat

Der Graphikbildschirm hat im Textmodus 24 Zeilen a 40. Jede Zeile in der Displaylist bekommt also eine 2 eingeschrieben (die ersten 24 Scanlines bleiben leer). Die erste Zeile hat dabei nicht den Wert 2 sondern 66 (64+2) - wobei die 64 einen Adressbefehl darstellt - nämlich den des Bildschirmanfanges. Es folgen dann die 23 nächsten Zeilen mit dem Wert 2 und danach kommt noch der Sprungbefehl mit Adresse des Displaylistanfanges.

Nun möchten wir folgendes machen: In jede Zeile schreibe ich statt einer 2 eine 66 (alles dezimal) rein mit der jeweiligen um eine Zeile höheren Adresse für den Bildschirm: 66 64 156 (40000) 66 104 156 (40040) 66 144 156 (40080) 66 184 156 66 224 156 .......... usw am Ende wieder der Sprungbefehl für den Start der Displaylist.

Als nächstes (mein Grobscroll) kopiere ich einfach die Displaylist um - verschiebe also alle Zeilen um eins nach oben: 66 104 156 66 144 156 66 184 156 66 224 156 66 08 157 ..... 66 64 156 Sprungbefehl Displaylistanfang

die letzte Zeile vor dem Sprungbefehl müsste dann wohl immer die vorherige erste sein. Damit bekomme ich ein fortlaufendes Grobscroll des gesamten Bildschirmes. Will ich jetzt einen Shooter damit machen, überschreibe ich einfach die komplette erste Zeile der Displaylist mit den neuen Landschaftsdaten (meine "letzte Reihe") - anschließend wird die erste Reihe durch Umsortierung der Displaylist letzte Reihe. Theoretisch könnte ich auch ganz einfach 24 verschiedene Displaylist mit entsprechend versetzten Zeilen vorbereiten und dann einfach nur jeweils von Displaylist 1 zu 2 zu 3 zu .... zu 24 umschalten. Die Displaylist wird dann noch so aufgebaut, daß der entsprechende Graphicmodus plus Feinscroll mit drin ist, der Grobscroll wird entsprechend immer einmal nach 7 Feinscrollaktionen durchgeführt.

So habe ich die Theorie verstanden.... das möchte ich demnächst mal umsetzen. Habe ich das korrekt verstanden?? Liebe Grüße Peter

AntwortZitat

In dem Beispiel von mir im "Return to Phobos"-Thread sieht das Scrolling wie folgt aus.

Scrolling.png

Nur in der letzten Zeile muss man aufpassen, da dort der Code $42 steht und nicht $62 !!! Deine Aussage trifft also zu.

Grüße Janko

Anhänge:

Mein 600XL ist virenfrei!

AntwortZitat

ha... so habe ich es verstanden! Cool! Das sieht ja prinzipiell viel einfacher und vor allem viel schneller aus... mit der Methode müsste man doch auch eine hochaufgelöste Graphik ganz schnell scrollen können!!
Wenn ich also eine Graphik mit 320x192 scrollen möchte, dann erstelle ich einfach 192 verschiedene Displaylisten (dann fällt ja auch noch die Rechenzeit für die Umkopiererei weg) und schalte die einfach hintereinander um und habe so einen hochaufgelösten schnellen Scroll ....

AntwortZitat

Hallo!

Einzelne Displaylisten bei 192 Zeilen? In jeder Liste sind doch min. 200 Bytes enthalten, oder? Also 192 Schritte x 200 Bytes = 38400 Bytes = 37,5kb = Atari voll 🙂

Oder mache ich jetzt einen Denkfehler?

Grüße Janko

Mein 600XL ist virenfrei!

AntwortZitat

Hallo Peter!

Irgendwie hast Du mich neugierig gemacht ein Scrolling für 320x192 Punkte hinzu bekommen 🙂 7,5k zu scrollen, kann man nur über Displaylist-Manipulation hinbekommen, aber wie? 192 Displayliste? Nein zu wenig Speicher 1 Liste Rotieren lassen, wie in meinem Beispiel für den Textmodus Gr. 0 ? Das wären fast 600 Bytes, die ständig 1/50s rotieren...

In Endeffekt habe ich nun zwei Displaylisten und lasse darin den MemScan wandern. Also $40 in der Displaylist mit der Angabe des Speicherbereiches. Es müssen nun 15 Bytes ständig neu gesetzt werden. Die Logik ist aufwendig, wird wegen unterschiedlichen Zuständen nur tlw. immer abgearbeitet. Insgesamt scheint das Scrolling gut zu funktionieren.

Hier mein Beispiel (Quick & Dirty 😀 ) :

; ############################################################################
; # Programm um den Bildschirm im Grafikmodus 8 (320x192) zu scrollen,
; # ohne den Speicherbereich zu verschieben
; #
; # ZeroPage A0 bis A5 werden für notwendige Zeiger genutzt
; # Der Bildschirmspeicher liegt ab Adresse $2150 bis $3F4F
; # Die Displaylist wird in den Bereich $2000 geschrieben
; #
; # Das Programm selbst beginnt ab Adresse $4000, direkt hinterm 
; # Bildschirmspeicher
; #
    org $4000

    icl 'Systemkonstanten.asm'

; ############################################################################
; ### Variabeln definieren

ScrMemZero  equ $a0
DListZero equ $a2
LastLine  equ $a4

ScrMemZ   .word $2150
DListZ    .word $2000

VScrollPos  .byte 99
VScrollCnt  .byte 00

; ############################################################################
; ### Daten definieren

; # DisplayList
; # Da der ANTIC nur 4K-Daten verarbeiten kann, muss man den Bildschirm in zwei
; # Bereiche aufteilen, die jeweils < 4096 Bytes haben.
; # Beim scrollen muss man dies berücksichtigen, weshalb ich in zwei Abschnitten
; # scrolle und deshalb 2 Displaylisten benötige
; # Jeweils mit vertauschten 4K Block
; # Um einen zusammenhängenden Bild-Bereich zu haben, beginnt man bei
; # $2150 und endet bei $3F4F

DList1    .byte $70,$70,$70
    .byte $4F,$50,$21       ; Der MEMSCAN wird hier beim scrollen
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F ; regelmäßig der neuen Situation angepasst
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F   
    .byte $4F,$00,$30       ; Dieser MEMSCAN wird "nach oben" wandern,
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F ; und immer auf die selbe Adresse zeigen
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F
    .byte $4F,$28,$3F         ; Dieser MEMSCAN wird einmalig korigiert und 
                ; anschließend wander er ebenfalls "hoch"
    .byte $41,$00,$20
    .byte $ff

DList2    .byte $70,$70,$70
    .byte $4F,$00,$30       ; Der MEMSCAN wird hier beim scrollen
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F ; regelmäßig der neuen Situation angepasst
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $4F,$50,$21       ; Dieser MEMSCAN wird "nach oben" wandern,
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F ; und immer auf die selbe Adresse zeigen 
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F
    .byte $0F,$0F
    .byte $4F,$D8,$2F       ; Dieser MEMSCAN wird einmalig korigiert und
                ; anschließend wander er ebenfalls "hoch"
    .byte $41,$00,$20
    .byte $ff

; ############################################################################
; # VSync
; # 
    .proc WaitVSync     ; VBI abwarten

    lda RTCLOK+2
Loop      cmp RTCLOK+2  
          beq Loop

    rts

    .end

; #############################################################################
; ### Speicherbereiche initialisieren

    .proc MemInI          

    lda ScrMemZ     ; Speicherbereich des Bildbildschirm
    sta ScrMemZero      ; in die ZeroPage kopieren
    lda ScrMemZ+1
    sta ScrMemZero+1    

    lda DListZ      ; Speicherbereich der Displaylist
    sta DListZero     ; in die ZeroPage kopieren
    lda DListZ+1
    sta DListZero+1   

      jsr ScreenMem     ; Bildschirm löschen (leeren)
      jsr DListLoad.First   ; Erste Displaylist einladen

    jsr WaitVSync     ; VBI abwarten

    lda DlistZ      ; Displaylist laden
          sta SDLSTL
    lda DlistZ+1
    sta SDLSTL+1

    jsr WaitVSync     ; Erneut VBI abwarten

    rts

    .end

; ############################################################################
; # Clear Screen
; # 
; # Sicherstellen, dass der Bildschirm sauber ist
; #
    .proc ScreenMem

    ldx #$00
    ldy #$00

    lda #$00      ; Routine um den Bildschirm
@   sta (ScrMemZero),y    ; zu löschen
    iny       ; Da insgesamt 7,5k gelöscht
    bne @-        ; werden müssen,
    inx         ; wird der Vorgang
    cpx #$1E      ; in mehrere 256er Läufe
    beq @+
    inc ScrMemZero+1
    jmp @-

@   inc ScrMemZero+1
    ldy #$B0      ; und einem Restlauf
@   dey       ; aufgeteilt
    cpy #$00
    beq @+
    lda #$00
    sta (ScrMemZero),y
    jmp @-

@   lda ScrMemZ     ; ZeroPage-Variable
    sta ScrMemZero      ; wieder herstellen
    lda ScrMemZ+1
    sta ScrMemZero+1

    rts

    .end

; ############################################################################
; # Displaylist einrichten
; # 
; # Es gibt zwei Displaylisten, die je nach Scrollposition eingeblendet werden müssen
; #
    .proc DListLoad

First   ldy #$00      ; Die Displaylist

Loop    lda DList1,y      ; muss in den Bereich ab $2000
    cmp #$ff      ; eingelesen werden
    beq @+        ; $ff ist dabei das Signal
    sta (DlistZero),y   ; das alles gelesen wurde
    iny
    jmp Loop

@   rts

Second    ldy #$00

Loop2   lda DList2,y      ; Ein zweiter Loop für die
    cmp #$ff      ; zweite Displaylist
    beq @+
    sta (DlistZero),y
    iny
    jmp Loop2

@   rts

    .end

; ############################################################################
; # VScroll - Routine um Grafikmodus 320 x 192 zu scollen 
; # 
    .proc VScroll
    lda move      ; In Move wird festgehalten
    cmp #$02      ; Ob man im ersten Durchlauf = 0 ist
    bcc @+        ; Mehr als Durchlauf der 1. Häfte hat = 1
    jmp SecondDLI     ; Oder gar in der 2. Hälfte sich befindet >2

@   lda VScrollPos      ; VScrollPos wieder verringert
    cmp #$06      ; bis zur 6, um nicht wichtige Einträge zu 
    beq Restore1      ; überschreiben
    dec VScrollPos      ; Bei 6 wird die 2. Displaylist aktiviert

    ldy #$03      ; y wird auf die 1 Memory-Scanzeile der Displaylist
    jsr DListSetMem     ; ausgerichtet und dieser dann hochgezählt
    ldy VScrollPos      ; y nun auf den ersten Wander-Memscan setzen
    jsr DListSet1UPF    ; und wandern lassen

    lda Move      
    cmp #$01
    beq @+
    ldy #199
    jmp FirstMove1      ; Im ersten Durchgang wird der 3. Memscan nur 
@   tya       ; verändert auf $2150
    clc
    adc #97       
    tay
    jsr DListSet1UPF    ; In weiteren Durchgängen wird dieser wandern

@   jmp Count

SecondDLI lda VScrollPos      ; für die zweite Displaylist prüfen, ob das 
    cmp #$06      ; Ende der Durchläufe erreicht ist    
    beq Restore2      ; Dann wieder auf Displaylist 1 zurück ändern
    dec VScrollPos

    ldy #$03      ; y wieder auf den 1 Memory-Scann der Displaylist
    jsr DListSetMem     ; setzen und hochzählen
    ldy VScrollPos      
    jsr DListSet1UPF    ; Hier wieder den 2. Scan wandern lassen

    lda Move      ; Erster Durchlauf der 2. Displaylist = 5
    cmp #$06
    beq @+
    ldy #199
    jmp FirstMove2      ; Im ersten Durchlauf nur die Adresse korrigieren
@   tya
    clc
    adc #93
    tay
    jsr DListSet1UPF    ; Im weiteren Durchlauf den Memscan wandern lassen

@   jmp Count     ; Zähler für "LastLine" wo neuer Inhalt reingeschrieben 
            ; werden kann
; -- Unterprogramme
FirstMove1  lda #$01      ; Move auf 1 setzen
    sta Move
    lda #$50      ; Speicherbereich auf $2150 setzen
    sta (DlistZero),y
    sta LastLine
    iny
    lda #$21
    sta (DlistZero),y
    sta LastLine+1
    rts 

Restore1  jsr DListLoad.Second    ; Zweite Displaylist aktivieren
    lda #103      ; Und Variabeln neu setzen
    sta VScrollPos      ; fürs wandern
    lda #5        ; Move auf 5
    sta Move
    lda #$d8      ; Speicher auf $2fd8 setzen
    sta LastLine
    lda #$2f
    sta LastLine+1
    rts   

FirstMove2  lda #$06      ; Move auf 6 setzen
    sta Move
    lda #$00      ; und Speicher auf $3000
    sta (DlistZero),y   ; ändern
    sta LastLine
    iny
    lda #$30
    sta (DlistZero),y
    sta LastLine+1
    rts   

Restore2  jsr DListLoad.First   ; Displaylist 1 wieder aktivieren
    lda #99       ; Variabeln neu setzen
    sta VScrollPos
    lda #0        ; Move wieder auf 0
    sta Move
    lda #$28      ; Speicherbereich auf $3f28 setzen
    sta LastLine
    lda #$3f
    sta LastLine+1
    inc VScrollCnt
    rts   

DListSet1UPF  lda #$4F      ; Routine, um die Memscan-Zeile
    sta (DlistZero),y   ; wandern zu lassen
    iny
    iny   
    lda (DlistZero),y
    dey
    sta (DlistZero),y
    iny
    iny   
    lda (DlistZero),y
    dey
    sta (DlistZero),y
    iny   
    lda #$0f
    sta (DlistZero),y   
    rts

DListSetMem clc       ; Nur den Speicher hochzählen
    iny       ; der im Memscan steht
    lda (DlistZero),y
    adc #$28
    sta (DlistZero),y
    iny
    lda (DlistZero),y
    adc #$00
@   sta (DlistZero),y 
    rts

Count   clc       ; LastLine berechnen
    lda LastLine
    adc #$28
    sta LastLine
    lda LastLine+1
    adc #$00
    sta LastLine+1
    rts

Move    .byte $00

    .end

; ############################################################################
; ### Spielfeld generieren 
; ###
; ### Irgendetwas darstellen
; ###

    .proc Spielfeld

HoleRichtung  lda RANDOM
    cmp #$55
    bcc GeheLinks
    cmp #$aa
    bcs GeheRechts

GeheGeradeaus lda #$00
    sta Richtung    
    rts

GeheLinks lda Richtung
    cmp #$02
    beq @+
    lda Links
    cmp LinksMin
    bcc @+
    lda #$01
    sta Richtung
    rts

@   lda #$00
    sta Richtung
    rts

GeheRechts  lda Richtung
    cmp #$01
    beq @-
    lda Rechts
    cmp RechtsMax
    bcs @-
    lda #$02
    sta Richtung
    rts       

Start   jsr HoleRichtung

    lda Richtung
    cmp #$01
    beq LLinks
    bcc GGeradeaus
    bcs RRechts
    rts

GGeradeaus  ldy #$00
    lda #$ff
@   sta (LastLine),y
    iny
    cpy Links
    bcc @-
    dey
    lda LinksFein
    sta (LastLine),y
    iny

    lda #$00
@   sta (LastLine),y
    iny
    cpy Rechts
    bcc @-

    lda RechtsFein
    ldy Rechts
@   sta (LastLine),y
    lda #$ff
    iny
    cpy #$28
    bcc @-
    rts

LLinks    lda LinksFein
    cmp #$00
    beq @+
    clc
    asl
    sta LinksFein
    eor #$ff
    sta RechtsFein
    jmp GGeradeaus
@   lda #$ff
    sta LinksFein
    lda #$00  
    sta RechtsFein    
    dec Links
    dec Rechts
    jmp GGeradeaus

RRechts   lda RechtsFein
    cmp #$00
    beq @+
    clc
    lsr
    sta RechtsFein
    eor #$ff
    sta LinksFein
    jmp GGeradeaus

@   lda #$ff
    sta RechtsFein    
    lda #$00  
    sta LinksFein
    inc Links
    inc Rechts
    jmp GGeradeaus

LinksFein .byte $ff
Links   .byte $14
RechtsFein  .byte $ff
Rechts    .byte $1E
Richtung  .byte $00   ; 0 = Geradeaus   1 = Links   2 = Rechts
LinksMin  .byte $08
RechtsMax .byte $20

    .end    

; #############################################################################
; ### Hauptprogramm

    .proc main

    jsr MemInI

    ;lda #$0A
    ;sta color2

Loop    jsr WaitVSync
    jsr VScroll
    jsr Spielfeld.Start


    jmp Loop    

    .end

; #############################################################################
    run main    

Gruß Janko

Mein 600XL ist virenfrei!

AntwortZitat

Guten Morgen Janko, ich habe mir das Listing gleich mal rübergezogen und ausprobiert... beeindruckend! Tatsächlich hatte ich mal ganz am Anfang mit Scrolling experimentiert unter Basic und das ging dort auch nur relativ zügig und schnell mit 2 verschiedenen Displays und dann umschalten... so hätte ich das jetzt nach meinem Projekt Phobos versucht - du hast das jetzt umgesetzt!! Das läuft echt gut. Respekt. Was ich noch besser finde ist, daß du auch noch einen Zufallsgenerator für eine einfache Landschaft eingebaut hast. Klasse!! Der Code scheint aufwendig, aber das Ergebnis ist ja entscheidend und der eigentliche Programmablauf um 8k zu scrollen läuft fast ohne CPU-Belastung. Im Grunde müsste man das jetzt noch für verschiedene interessante Grafikmodi umsetzen und veröffentlichen, dann muss das nicht jeder selber neu "erfinden". Klasse gemacht, Janko!!

Ich habe gestern ein wenig Zeit gehabt und neben der ganzen schlimmen Berichterstattung aus der Welt und vor allem aus der Ukraine mein Projekt weitergemacht. Neue Items sind drin - die Schranken! Dabei wollte ich die Schranken so gestalten, daß sie ständig animiert sind und dann als weiteres Item relativ einfach in die Landschaft reingeprintet werden können über meine Routine "letzte Reihe". Die Schranke muss in vernünftiger Zeit ein- bzw. ausgeschaltet werden, dieses Timing hat mich etwas Mühe und Zeit gekostet. Am Ende habe ich es ans "blinken" gekoppelt und mit ein paar Anpassungen läuft das jetzt ganz gut. Jetzt kommt noch eine weitere "Laufschranke" und noch weitere Landschaft und noch ein paar Sonderitems.

Für das Endlevel muss ich mir noch was schönes ausdenken...

Und danach gehe ich das nächste Projekt an... es wird wieder ein Shooter, der aber ganz anders aufgebaut sein soll...
Liebe Grüße Peter

AntwortZitat